#include <sys/types.h>
#include <sys/param.h>
#include <sys/socket.h>
#include <sys/file.h>
#include <sys/time.h>
#include <sys/signal.h>

#include <netinet/in_systm.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <netinet/ip_icmp.h>
#include <netdb.h>
#include <unistd.h>
#include <stdio.h>
#include <ctype.h>
#include <errno.h>
#include <string.h>

/**
* @author Kristina Kurian
* @date 1-November-2011
* @name icmpaddrmask
* @usage icmpaddrmask <IP>
*/

#define	MAXIPLEN	60
#define	MAXICMPLEN	76

struct sockaddr	whereto;
int		datalen = 12;
int		s;
u_char		outpack[65468];
char		*hostname;
int		response = 0;

void procpack(char *buf, int cc, struct sockaddr_in *from)
{
    int		i, hlen;
    struct icmp	*icp;
    struct ip	*ip;
    struct timeval	tvdelta;

    ip = (struct ip *)buf;
    hlen = ip->ip_hl << 2;
    if (cc < hlen + ICMP_MINLEN) {
        printf("ERROR: packet too short\n");
        return;
    }

    cc -= hlen;
    icp = (struct icmp *)(buf + hlen);


    if (icp->icmp_type == ICMP_MASKREPLY) {
        if (cc != ICMP_MASKLEN)
            printf("cc = %d, expected cc = 12\n", cc);
        if (icp->icmp_seq != 12345)
            printf("received sequence # %d\n", icp->icmp_seq);
        if (icp->icmp_id != getpid())
            printf("received id %d\n", icp->icmp_id);

        printf("received mask = %08x, from %s\n",
               ntohl(icp->icmp_mask),
               inet_ntoa(*(struct in_addr *) &from->sin_addr.s_addr));
        response++;
    }
}

int check_sum(u_short *addr,int len)
{
    int nleft = len;
    u_short *w = addr;
    int sum = 0;
    u_short answer = 0;

    while (nleft > 1)  {
        sum += *w++;
        nleft -= 2;
    }

    if (nleft == 1) {
        *(u_char *)(&answer) = *(u_char *)w ;
        sum += answer;
    }

    sum = (sum >> 16) + (sum & 0xffff);
    sum += (sum >> 16);
    answer = ~sum;
    return(answer);
}

void sig_alrm(int signo)
{
    if (response == 0) {
        printf("timeout\n");
    }
}

int main(int argc, char **argv) {
    int			i, ch, fdmask, hold, packlen, preload;
    struct hostent		*hp;
    struct sockaddr_in	*to;
    struct protoent		*proto;
    u_char			*packet;
    char			*target, hnamebuf[MAXHOSTNAMELEN];

    if (argc != 2) {
        printf("ERROR: Wrong Initial parametr\n");
        return 1;
    }
    target = argv[1];

    bzero((char *)&whereto, sizeof(struct sockaddr));
    to = (struct sockaddr_in *)&whereto;
    to->sin_family = AF_INET;

    to->sin_addr.s_addr = inet_addr(target);
    if (to->sin_addr.s_addr != (u_int)-1) {
        hostname = target;
    } else {
        hp = gethostbyname(target);
        if (!hp) {
            printf("ERROR: Unknown host\n");
            return 1;
        }
        to->sin_family = hp->h_addrtype;
        bcopy(hp->h_addr, (caddr_t)&to->sin_addr, hp->h_length);
        strncpy(hnamebuf, hp->h_name, sizeof(hnamebuf) - 1);
        hostname = hnamebuf;
    }

    packlen = datalen + MAXIPLEN + MAXICMPLEN;
    if ( (packet = (u_char *)malloc((u_int)packlen)) == NULL) {
        printf("ERROR: problem with memory\n");
        return 1;
    }

    if ( (proto = getprotobyname("icmp")) == NULL) {
        printf("ERROR: unknown protocol icmp\n");
        return 1;
    }

    if ( (s = socket(AF_INET, SOCK_RAW, proto->p_proto)) < 0) {
        printf("ERROR: could open socket\n");
        return 1;
    }


    signal(SIGALRM, sig_alrm);
    alarm(20);

    int		j, cc;
    struct icmp	*icp;

    icp = (struct icmp *)outpack;
    icp->icmp_type = ICMP_MASKREQ;
    icp->icmp_code = 0;
    icp->icmp_cksum = 0;
    icp->icmp_seq = 12345;
    icp->icmp_id = getpid();

    icp->icmp_mask = 0;

    cc = ICMP_MASKLEN;

    icp->icmp_cksum = check_sum((u_short *)icp, cc);

    j = sendto(s, (char *)outpack, cc, 0, &whereto,
               sizeof(struct sockaddr));

    if (j < 0) {
            printf("ERROR: sendto\n");
    }
    for (;;) {
        struct sockaddr_in	from;
        int			cc, fromlen;

        fromlen = sizeof(from);
        if ( (cc = recvfrom(s, (char *)packet, packlen, 0,
                            (struct sockaddr *)&from, &fromlen)) < 0) {
            continue;
        }
        procpack((char *)packet, cc, &from);
    }
}

 
